/*
 * ULSF Sagittarius IDA
 * Copyright (C) 2012, John Pritchard, all rights reserved.
 * 
 * This program is free software: you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 */
package sagittarius.panel;


public class Section
    extends Object
{
    /**
     * Coordinates
     */
    public final float[][] p;
    /**
     * Number of coordinates, and leading edge index
     */
    public final int np, nle;
    /**
     * Description
     */
    public final float chord, aspect, area;
    /**
     * Centroid 
     */
    public final float[] c;
    /**
     * Difference from centroid position to abstact panel centroid
     * position <i>(a = p + d)</i>.
     */
    public final float[] d;


    public Section(Panel p, float z, float chord){
        this(p,z,chord,chord);
    }
    public Section(Panel p, float z, float chord, float aspect){
        super();
        Panel.Surface s = p.surface;

        this.chord = chord;
        this.aspect = aspect;
        this.np = s.npoints;
        this.nle = s.nle;

        /*
         * Copy and scale (X,Y) [abstract of final position]
         */
        final int np = s.npoints;
        final float[] sxb = s.xb;
        final float[] syb = s.yb;

        float[] ax = new float[np],
            ay = new float[np];

        for (int cc = 0; cc < np; cc++){
            ax[cc] = (sxb[cc]*chord);
            ay[cc] = (syb[cc]*aspect);
        }
        /*
         * Calculate area and centroid
         */
        float area = 0.0f;
        float acx = 0.0f, acy = 0.0f;
        {
            float ax0, ax1, ay0, ay1, f;

            for (int cc = 0, zz = (np-2); cc < zz; cc++){
                ax0 = ax[cc];
                ax1 = ax[cc+1];

                ay0 = ay[cc];
                ay1 = ay[cc+1];

                f = (ax0*ay1 - ax1*ay0);

                acx += ((ax0+ax1)*f);

                acy += ((ay0+ay1)*f);

                area += f;
            }
        }

        area /= 2.0f;

        this.area = Math.abs(area);

        if (area == area && 0.0f != area){

            area *= 6.0f;

            acx /= area;
            acy /= area;
        }
        this.c = new float[]{0.0f,0.0f,z};

        this.d = new float[]{acx,acy,0.0f};

        /*
         * Offset to centroid at (0,0,0)
         */
        float[][] vp = new float[np][];

        for (int cc = 0; cc < np; cc++){

            float x = (ax[cc] - acx);
            float y = (ay[cc] - acy);
            vp[cc] = new float[]{x,y,z};
        }
        this.p = vp;
    }


    public float getXTE(){
        return this.p[0][0];
    }
    public float getXLE(){
        return this.p[this.nle][0];
    }
    public float getX(int idx){
        return this.p[idx][0];
    }
    public float getY(int idx){
        return this.p[idx][1];
    }
    public float getZ(){
        return this.p[0][2];
    }
    /**
     * @param x Domain coordinate
     * @return Zero, one or two points in this coordinate.
     */
    public float[][] station(final float x){

        final int nle = this.nle;
        final int nte = (this.p.length-1);

        for (int cc = 0; cc < nle; cc++){

            float[] v0 = this.p[cc];
            float[] v1 = this.p[cc+1];

            float xL0 = v0[0];
            float xL1 = v1[1];

            switch (BinLower(xL0,x,xL1)){
            case -1:
                return null;
            case 0:
                final float z = this.getZ();

                float yL0 = v0[1];
                float yL1 = v1[1];

                for (cc = nle; cc < nte; cc++){

                    v0 = this.p[cc];
                    v1 = this.p[cc+1];

                    float xU0 = v0[0];
                    float xU1 = v1[0];

                    switch (BinUpper(xU0,x,xU1)){
                    case -1:

                        float iy = LIY(xL0,xL1,yL0,yL1,x);

                        return new float[][]{new float[]{x,iy,z}};

                    case 0:
                        float yU0 = v0[1];
                        float yU1 = v1[1];

                        float iLy = LIY(xL0,xL1,yL0,yL1,x);
                        float iUy = LIY(xU0,xU1,yU0,yU1,x);

                        if (iLy == iUy)
                            return new float[][]{
                                new float[]{x,iLy,z}
                            };
                        else
                            return new float[][]{
                                new float[]{x,iLy,z},
                                new float[]{x,iUy,z}
                            };
                    }
                }
            }
        }
        return null;
    }
    /**
     * Bounds inclusion test for lower surface scan
     */
    private final static int BinLower(float x0, float ob, float x1){
        if (x0 < ob)
            return -1;
        else if (x1 <= ob)
            return 0;
        else
            return 1;
    }
    /**
     * Bounds inclusion test for upper surface scan
     */
    private final static int BinUpper(float x0, float ob, float x1){
        if (x0 > ob)
            return -1;
        else if (x1 >= ob)
            return 0;
        else
            return 1;
    }
    /**
     * Linear interpolation of Y
     */
    private final static float LIY(float x0, float x1, float y0, float y1, float x){
        if (x0 == x)
            return y0;
        else if (x1 == x)
            return y1;
        else {
            float y = (y0+((y1-y0)*((x-x0)/(x1-x0))));
            if (y != y)
                throw new IllegalStateException();
            else
                return y;
        }
    }
    public final static Section[] Add(Section[] list, Section item){
        if (null == item)
            return list;
        else if (null == list)
            return new Section[]{item};
        else {
            int len = list.length;
            Section[] copier = new Section[len+1];
            System.arraycopy(list,0,copier,0,len);
            copier[len] = item;
            return copier;
        }
    }

}
